package com.ljm.lock.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ljm.common.util.IpUtils;
import com.ljm.lock.dto.LockValue;
import com.ljm.lock.service.ILockService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.List;

/**
 * @Author: ChenHuaMing
 * @Date: 2020/10/9 14:53
 * @Description: 分布式锁（redis实现）
 */
public class RedisLockServiceImpl implements ILockService {
    private final Logger logger= LoggerFactory.getLogger(RedisLockServiceImpl.class);
    private StringRedisTemplate stringRedisTemplate;
    private RedisScript<Boolean> defaultRedisScript;
    private String timeOut;
    private ObjectMapper objectMapper=new ObjectMapper();
    private final String PREFIX="hadoop:lock:";

    public RedisLockServiceImpl(StringRedisTemplate stringRedisTemplate, RedisScript<Boolean> defaultRedisScript, String timeOut) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.defaultRedisScript = defaultRedisScript;
        this.timeOut = timeOut;
    }

    @Override
    public boolean getLock(String key,String expireTime) {
        if(StringUtils.isEmpty(expireTime)){
            expireTime=getTimeOut();
        }
        long lockTime = System.currentTimeMillis();
        LockValue redisLockValue = new LockValue(key,getThreadId(), lockTime, (lockTime + Long.valueOf(expireTime)));
        try {
            String value = objectMapper.writeValueAsString(redisLockValue);
            List<String> keys = Arrays.asList(PREFIX+key, value);
            Boolean execute = stringRedisTemplate.execute(defaultRedisScript, keys, expireTime);
            return execute == null?false:execute;
        } catch (JsonProcessingException e) {
            logger.error("transport lockValue error in lock",e);
            return false;
        }
    }

    @Override
    public boolean unlock(String key) {
        String lockValueStr = stringRedisTemplate.opsForValue().get(PREFIX + key);
        try {
            if(!StringUtils.isEmpty(lockValueStr)){
                LockValue lockValue = objectMapper.readValue(lockValueStr, LockValue.class);
                if(!getThreadId().equals(lockValue.getThreadId())){
                    throw new RuntimeException("线程id不一致,不允许解锁");
                }
                if(!lockValue.getBussId().equals(key)){
                    throw new RuntimeException("业务id不一致,不允许解锁");
                }
                return stringRedisTemplate.delete(PREFIX+key);
            }else{
                return false;
            }
        } catch (JsonProcessingException e) {
            logger.error("transport lockValue error in unlock",e);
            return false;
        }
    }

    private String getThreadId(){
        return new StringBuilder()
                .append(IpUtils.getLocalIpAddress())
                .append("-")
                .append(Thread.currentThread().getId()).toString();
    }

    @Override
    public String getTimeOut() {
        return timeOut;
    }
}
